<?php

class ApiAgent extends CApplicationComponent
{
    use util_array;
    private $_json;
    private $_jsonDecode;

    private $_errnos;

    private $_ip;

    private $_service;
    private $_action;
    private $_classname;
    private $_method;
    private $_params;
    private $_errno;
    private $_result;
    private $_log_ignore;

    const API_ERROR_INTERNAL = 100;
    const API_ERROR_POST_DATA = 101;
    const API_ERROR_TOKEN_INVALID = 102;
    const API_ERROR_APP_CLOSE = 103;
    const API_ERROR_NO_AUTH = 300;
    const API_ERROR_URL = 402;
    const API_IP_NO_ALLOW = 403;
    const API_ERROR_NO_SERVICE = 404;
    const API_ERROR_UNKNOWN = 900;

    const CLASS_SUFFIX = 'Service';
    const CLASS_PREFIX = 'Api';

    public function __construct()
    {
        if (empty($this->_log_ignore)) {
            $this->_log_ignore = array('');
        }
    }

    public function init()
    {
        parent::init();
        $this->_errnos = array(
            self::API_ERROR_INTERNAL => Yii::t("api", 'Internal error'),
            self::API_ERROR_POST_DATA => Yii::t("api", 'Invalid post data format'),
            self::API_ERROR_TOKEN_INVALID => Yii::t("api", 'Invalid token'),
            self::API_ERROR_APP_CLOSE => Yii::t("api", 'app status is close.'),
            self::API_ERROR_NO_AUTH => Yii::t("api", 'Authentication failed'),
            self::API_ERROR_NO_SERVICE => Yii::t("api", 'Service not found'),
            self::API_ERROR_URL => Yii::t("api", 'Url error '),
            self::API_IP_NO_ALLOW => Yii::t("api", 'IP is not allow'),
            self::API_ERROR_UNKNOWN => Yii::t("api", 'Unknown Error when call api'),
        );
        $this->_log_ignore = array('user.is_api_auth');
        $this->_errno = 0;
        $this->_result = array('errno' => $this->_errno);
    }

    public function call($json)
    {
        Yii::log(json_encode($json), CLogger::LEVEL_INFO, __METHOD__);
        $this->_json = $json;
        if ($this->validate()) {
            try {
                $this->_result['data'] = call_user_func_array(array($this->_classname, $this->_method), array
                ($this->_params, null));
            } catch (Exception $e) {
                $this->error(self::API_ERROR_INTERNAL);
                Yii::log($e->getMessage(), CLogger::LEVEL_ERROR, __METHOD__);
            }
        }
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Headers: Content-Type');
        header('Access-Control-Allow-Methods: POST');
        header('Access-Control-Max-Age: 1000');
        header('Content-Type: application/json');

        return json_encode($this->_result);
    }

    private function validate()
    {
        //check json data
        $this->_jsonDecode = json_decode($this->_json, true);
        $this->_ip = Curl::getUserIp();
        $info = $this->getVersionServiceAction();
        $this->_params = isset($this->_jsonDecode['params']) ? $this->_jsonDecode['params'] : array();

        if (empty($info['service']) || empty($info['action'])) {
            $this->error(self::API_ERROR_URL);
            return false;
        }
        $this->_service = $info['service'];
        $this->_action = $info['action'];

        $this->_classname = self::CLASS_PREFIX . ucfirst($info['service']) . self::CLASS_SUFFIX;
        $this->_method = $this->_action . 'Action';


        if (empty($this->_jsonDecode)) {
            $this->error(self::API_ERROR_POST_DATA);
            return false;
        }

        //check auth
        if (!$this->auth()) {
            $this->error(self::API_ERROR_NO_AUTH);
            return false;
        }
        //check class
        if (!class_exists($this->_classname)) {
            $this->error(self::API_ERROR_NO_SERVICE);
            return false;
        }
        //check method
        $available_methods = array();
        foreach (get_class_methods($this->_classname) as $method) {
            $available_methods[strtolower($method)] = $method;
        }
        if (empty($available_methods[strtolower($this->_method)])) {
            $this->error(self::API_ERROR_NO_SERVICE);
            return false;
        } else {
            $this->_method = $available_methods[strtolower($this->_method)];
        }
        return true;
    }

    private function getVersionServiceAction()
    {
        $result = array('service' => '', 'action' => '',);
        $urlManager = new CUrlManager();
        $urlManager->urlFormat = 'path';
        $urlManager->showScriptName = 'false';
        $urlManager->addRules(array(
            'api/<service:\w+>/<action:\w+>' => 'api/<service>/<action>',
        ));
        $route = $urlManager->parseUrl(Yii::app()->request);
        if (!empty($route)) {
            $segs = explode('/', $route);
            if (count($segs) == 3) {
                if ($segs[0] == 'api') {
                    $result['service'] = $segs[1];
                    $result['action'] = $segs[2];
                }
            }
        }
        return $result;
    }

    private function auth()
    {
        return true;
    }

    private function error($errno)
    {
        if (!isset($this->_errnos[$errno])) {
            $errno = self::API_ERROR_UNKNOWN;
        }
        $this->_errno = $errno;
        $this->_result['errno'] = $errno;
        $this->_result['errmsg'] = $this->_errnos[$errno];
    }

    public function setLogIgnore($val)
    {
        if (is_array($val)) {
            $this->_log_ignore = $val;
        } elseif (is_string($val)) {
            $this->_log_ignore = array($val);
        }
    }

    public function getServiceAction()
    {
        $urlManager = new CUrlManager();
        $urlManager->urlFormat = 'path';
        $urlManager->showScriptName = 'false';
        $urlManager->rules = array(
            'api/<service:\w+>/<action:\w+>' => 'api/<service>/<action>',
        );
        $route = Yii::app()->urlManager->parseUrl(Yii::app()->request);

        return $route;
    }

    public function getLogIgnore()
    {
        return $this->_log_ignore;
    }
}